package org.jeecg.common.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.tuple.Pair;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 统计数据工具类
 *
 * @author zjarlin
 * @since 2023/04/26
 */
public class Stats {
    /**
     * 柱状图工具类(分组统计)
     *
     * @param list   列表
     * @param getFun 得到乐趣 入参
     * @return {@link List }<{@link JSONObject }>
     * @author zjarlin
     * @since 2023/04/26
     */
    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Function<T, R>... getFun) {
        Pair<Function<T, R>, Function<R, U>>[] pairs = Arrays.stream(getFun).map(e -> {
            return Pair.of(e, Function.identity());
        }).toArray(Pair[]::new);

        return groupAndCount(list, pairs);
    }

    /**
     * 集团和计数
     *
     * @param list  列表
     * @param paris 巴黎 入参
     * @return {@link List }<{@link JSONObject }>
     * @author zjarlin
     * @since 2023/04/26
     */
    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Pair<Function<T, R>, Function<R, U>>... paris) {
        return Stream.iterate(0, (i) -> ++i).limit(paris.length).map(i -> {
            int label = i + 1;
            Pair<Function<T, R>, Function<R, U>> paris1 = paris[i];
            Function<T, R> getFun = paris1.getLeft();
            Function<R, U> reMappingFun = paris1.getRight();
            List<JSONObject> jsonObjects = groupAndCount(list, getFun, reMappingFun);
            return new JSONObject(new LinkedHashMap<>()) {{
                put("label", label);
                put("value", jsonObjects);
            }};
        }).collect(Collectors.toList());
    }

    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Function<T, R> getFun, Function<R, U> reMappingFun) {
        Map<R, Long> collect = list.stream().filter(e -> ObjectUtil.isNotEmpty(getFun.apply(e))).collect(Collectors.groupingBy(
                getFun
                , Collectors.counting()));
        return collect.entrySet().stream().map(e -> {
            R x = e.getKey();
            U apply = reMappingFun.apply(x);
            return new JSONObject(new LinkedHashMap<>()) {{
                put("x", apply);
                put("y", e.getValue());
            }};

        }).collect(Collectors.toList());
    }


    /**
     * 有范围时间的折线图,如果没范围,统计recent天的数据
     *
     * @param list    列表
     * @param getType 得到类型
     * @param getDate 获取日期
     * @param begDate 请求日期
     * @param endDate 结束日期 入参
     * @return {@link List }<{@link JSONObject }>
     * @author zjarlin
     * @since 2023/05/15
     */
    public static <T> List<JSONObject> getRangeDaysTrend(List<T> list, Function<T, String> getType, Function<T, LocalDate> getDate, LocalDate begDate, LocalDate endDate, int recent) {
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        LocalDate now = LocalDate.now();

        boolean b = Stream.of(begDate, endDate).allMatch(Objects::isNull);
        recent = Math.toIntExact(b ? recent : begDate.toEpochDay() - endDate.toEpochDay());
        if (b) {
            begDate = now.minusDays(recent - 1);
            endDate = now;
        } else if (Objects.isNull(begDate)) {
            begDate = now;
            endDate = now.plusDays(recent - 1);
        } else if (Objects.isNull(endDate)) {
            begDate = now.minusDays(recent - 1);
            endDate = now;
        }

        List<LocalDate> collect = Stream.iterate(begDate, i -> i.plusDays(1))
                .limit(endDate.toEpochDay() - begDate.toEpochDay() + 1)
//                .map(i -> {
//                    LocalDate now = LocalDate.now();
//                    LocalDate localDate = now.minusDays(i);
//                    return localDate;
//                })
                .collect(Collectors.toCollection(LinkedList::new));

        Map<String, ConcurrentMap<LocalDate, Long>> collect3 = list.stream()
                .filter(e -> {
                    String apply = getType.apply(e);
                    return StrUtil.isNotBlank(apply);
                })
                .filter(e -> {
                    boolean contains1 = CollUtil.contains(collect, getDate.apply(e));
                    return contains1;
                })
                .collect(Collectors.groupingBy(
                        getType,
                        Collectors.toConcurrentMap(
                                getDate,
                                e -> 1L,
                                Long::sum
                        )
                ));

        List<JSONObject> collect2 = collect3.entrySet().stream().map(e -> {
            String label = e.getKey();

            List<JSONObject> collect1 = collect.stream().map(date -> {
                Long value = e.getValue().getOrDefault(date, 0L);
                String string = date.toString();
                return new JSONObject() {{
                    put("x", string);
                    put("y", value);
                }};
            }).collect(Collectors.toList());

            return new JSONObject() {{
                put("label", label);
                put("value", collect1);
            }};

        }).collect(Collectors.toList());

        return collect2;
    }


    public static <T> List<JSONObject> getRecentTrend(List<T> list, Function<T, String> getType, Function<T, LocalDate> getDate, int recent) {

        LocalDate now = LocalDate.now();
        LocalDate begDate = now.minusDays(recent - 1);
        LocalDate endDate = now;
        return getRangeDaysTrend(list, getType, getDate, begDate, endDate, recent);

    }

    /**
     * 15天折线图
     *
     * @param list    列表
     * @param getType 得到类型
     * @param getDate 获取日期 入参
     * @return {@link List }<{@link JSONObject }>
     * @author zjarlin
     * @since 2023/04/26
     */
    public static <T> List<JSONObject> get15DaysTrend(List<T> list, Function<T, String> getType, Function<T, LocalDate> getDate) {
        int current = 15;
        List<LocalDate> collect = Stream.iterate(current - 1, i -> --i)
                .limit(current)
                .map(i -> {
                    LocalDate now = LocalDate.now();
                    LocalDate localDate = now.minusDays(i);
                    return localDate;
                }).collect(Collectors.toList());

        Map<String, Map<LocalDate, Long>> map = list.stream()
                .filter(e -> {
                    String apply = getType.apply(e);
                    return StrUtil.isNotBlank(apply);
                })
                .filter(e -> {
                    boolean contains1 = CollUtil.contains(collect, getDate.apply(e));
                    return contains1;
                })
                .collect(Collectors.groupingBy(
                        getType,
                        Collectors.groupingBy(
                                //e -> {
                                //    LocalDate apploy = getDate.apply(e);
                                //    if (collect.contains(apploy)) {
                                //        return apploy;
                                //    }
                                //    return null;
                                //},
                                getDate,
                                Collectors.counting()
                        )
                ));

        List<JSONObject> collect2 = map.entrySet().stream().map(e -> {
            String label = e.getKey();

            List<JSONObject> collect1 = e.getValue().entrySet().stream().map(x -> {
                LocalDate x1 = x.getKey();
                String string = x1.toString();
                Long value = x.getValue();
                return new JSONObject() {{
                    put("x", string);
                    put("y", value);
                }};
            }).collect(Collectors.toList());
            return new JSONObject() {{
                put("label", label);
                put("value", collect1);
            }};

        }).collect(Collectors.toList());

        return collect2;
    }


}
